/*-+-*****************************************************************************
 Copyright (C) 2009-2012 Oceanside Software Corporation (R)
 Dallas, Texas

 Author       : Jason T. Brower
 Purpose      : This file is part of a point-of-sale software package
                originally written for Table-Service Restaurants.
              
 Date         : July 8th of 2012
              
 History      : The application that this software file belongs to was 
                written over a 3-Year period.  The first version was written
                in C# .Net 3.5 using Windows Forms.  Being new to .Net at
                the time, I soon learned of Windows Presentation Foundation,
                liked the graphics capabilities and databinding so I
                migrated to WPF.  When the .Net 4.0 framework was released
                the application was migrated from 3.5 to 4.0. Now today,
                .Net 4.5 RC is out and I was already in process of migrating
                before I made this public.
                
Excuse Me     : Please excuse the coding style, it is not representative of
                my typical work.  During the three years I was under a very
                tight budget, constantly running out of funding and was 
                working 18 hour days hyped up on 5-Hour Energy.  The 
                design is not textbook.  It is monolythic because of
                previous issues I will not get into.  There are few comments
                in the code and the ones that are there may be incorrect.
                
What it is not: This is not the exact version that I took through PA-DSS 2.0
                nor is it the exact version running at my customer sites.
                I removed some details for security purposes including some
                encryption algorithms.  The reporting was removed because
                I was including a third party library that cannot be 
                published with this Open Source.
                
What it is    : This application has been running at high volume restaraunts
                for over a year.  It never freezes, crashes or does anything
                unexpected.  So long as it is built, installed and configured
                correctly, it is a strong system that we can all enhance.

Contact       : Please note that I can no longer work for free.  However, you
                can reach me at information@oceansidesw.com.                                                      

*********************************************************************************/
using System;
using System.Collections.Generic;
using System.Management;
using System.Printing;
using System.Linq;

namespace Oceanside
{
    public class PrintQueueStatus
    {
        /// <summary>
        /// Used to track a print jobs status as it relates to the queue.
        /// </summary>
        public PrintQueueStatus()
        {
            ///The following to initialization values are arb numbers using a date
            ///from the past and a queue depth we are likely to never hit.  The first
            ///status will make it appear as if the queue is moving when we check it.
            ///Then after, the real values will be updated and we can see if the queue
            ///is moving.
            this.oldestJobTime = new DateTime(1973, 9, 4);
            this.queueDepth = 10000;
        }

        public PrintQueueStatus(DateTime dt, int queueDepth)
        {
            this.oldestJobTime = dt;
            this.queueDepth = queueDepth;
        }

        DateTime oldestJobTime;
        public DateTime OldestJobTime
        {
            get { return oldestJobTime; }
            set { oldestJobTime = value; }
        }

        Int32 queueDepth;
        public Int32 QueueDepth
        {
            get { return queueDepth; }
            set { queueDepth = value; }
        }
    }

    /// <summary>
    /// Tracks the queued print jobs.
    /// </summary>
    public class QueuedPrintJob
    {
        static object _queueLocker = new object();
        static List<QueuedPrintJob> _queuedJobs = new List<QueuedPrintJob>();

        public bool IsLocalJob
        {
            get { return printServer == null; }
        }

        public static void QueueJob(Int32 id, string jobName, string printerName, string printServer, string printerPath)
        {
            QueuedPrintJob qpj = new QueuedPrintJob();
            qpj.jobId = id;
            qpj.jobName = jobName;
            qpj.printerName = printerName;
            qpj.timeQueued = DateTime.Now;
            qpj.printServer = printServer;
            qpj.printerPath = printerPath;

            lock (_queueLocker)
            {
                _queuedJobs.Add(qpj);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="printerPath"></param>
        /// <returns></returns>
        public static PrintQueue GetPrinterStatus(string printerPath)
        {
            try
            {
                ///Make sure we have access to the share by providing credentials.
                string pServer = Constants.PrintServerPath;
                PrintRaw.LoginToShare(pServer, Constants.PrintServerUserName, Constants.PrintServerPassword);

                using (PrintServer printServer = new PrintServer(pServer))
                {
                    using (PrintQueueCollection printServerQueues = printServer.GetPrintQueues())
                    {
                        if (printServerQueues != null && printServerQueues.Count() != 0)
                        {
                            foreach (PrintQueue pq in printServerQueues)
                            {
                                if (pq.FullName.Equals(printerPath))
                                {
                                    return pq;
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Logging.LogException(e);
            }
            return null;
        }

        private static void DequeueJob(Int32 id)
        {
            QueuedPrintJob pjob = null;

            foreach (QueuedPrintJob job in _queuedJobs)
            {
                if (id == job.JobId)
                {
                    pjob = job;
                }
            }
            if (pjob != null)
            {
                _queuedJobs.Remove(pjob);
            }
        }

        public static List<QueuedPrintJob> UpdateQueueStatus()
        {
            List<QueuedPrintJob> staleQueue = new List<QueuedPrintJob>();
            List<QueuedPrintJob> completeQueue = new List<QueuedPrintJob>();

            try
            {
                lock (_queueLocker)
                {
                    foreach (QueuedPrintJob pj in _queuedJobs)
                    {
                        ///If the job is not in the queue, then pull it.
                        PrintQueueStatus pqs = GetPrintJobStatus(pj);
                        if (pqs == null)
                        {
                            completeQueue.Add(pj);
                        }
                        ///If the job is still in the queue, check the time stamp.
                        else
                        {
                            ///If the depth of the remote queue is equal to or greater than last time we
                            ///checked, that does not mean an error, but it means the queue is in fact starting
                            ///to backup.
                            if (pqs.QueueDepth >= pj.lastStatus.QueueDepth)
                            {
                                ///If the time of the oldest print job in the queue is equal to the time of the oldest
                                ///job in the queue the last time we checked, then we have a queue that is not moving.
                                if (pqs.OldestJobTime == pj.lastStatus.OldestJobTime)
                                {
                                    ///Update the queue status
                                    pj.lastStatus = pqs;
                                    staleQueue.Add(pj);
                                }
                            }

                            ///Update the queue status
                            pj.lastStatus = pqs;
                        }
                    }

                    ///Now cleanout the queue.
                    foreach (QueuedPrintJob qpj in completeQueue)
                    {
                        _queuedJobs.Remove(qpj);
                    }
                }
            }
            catch (Exception e)
            {
                Logging.LogException(e);
            }

            return staleQueue;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="jobId"></param>
        /// <param name="host"></param>
        /// <returns></returns>
        private static PrintQueueStatus GetPrintJobStatus(QueuedPrintJob pj)
        {
            PrintQueueStatus status = null;
            ConnectionOptions options = new ConnectionOptions();
            options.Impersonation = ImpersonationLevel.Impersonate;
            options.Username = "oceanside";
            options.Password = Constants.PrintServerPassword;

            ManagementScope scope = null;

            if (pj.IsLocalJob)
            {
                scope = new ManagementScope(pj.printServer + "\\root\\cimv2");
            }
            else
            {
                scope = new ManagementScope(pj.printServer + "\\root\\cimv2", options);
            }
            scope.Connect();

            //Query system for Operating System information
            ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_PrintJob WHERE JobId = " + pj.jobId);

            using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
            {
                using (ManagementObjectCollection queryCollection = searcher.Get())
                {
                    if (queryCollection != null && queryCollection.Count != 0)
                    {
                        ///If the print job is still in the queue, then let's do some checks to see if the
                        ///queue is moving.  Start by grabbing all of the print jobs.
                        ObjectQuery getAllQuery = new ObjectQuery("SELECT * FROM Win32_PrintJob");
                        using (ManagementObjectSearcher getAllSearcher = new ManagementObjectSearcher(scope, getAllQuery))
                        {
                            using (ManagementObjectCollection allPrintJobsCollection = getAllSearcher.Get())
                            {
                                DateTime oldestTime = DateTime.Now;
                                foreach (System.Management.ManagementObject oReturn in allPrintJobsCollection)
                                {
                                    string stringTime = oReturn["TimeSubmitted"].ToString();
                                    int len = stringTime.IndexOf('.');
                                    stringTime = stringTime.Substring(0, len);
                                    DateTime dt = DateTime.ParseExact(stringTime, "yyyyMMddHHmmss", null);
                                    if (dt < oldestTime)
                                    {
                                        oldestTime = dt;
                                    }
                                }
                                status = new PrintQueueStatus(oldestTime, allPrintJobsCollection.Count);
                            }
                        }
                    }
                }
            }
            return status;
        }

        Int32 jobId;
        public Int32 JobId
        {
            get { return jobId; }
            set { jobId = value; }
        }

        String jobName;
        public String JobName
        {
            get { return jobName; }
            set { jobName = value; }
        }

        string printerName;
        public string PrinterName
        {
            get { return printerName; }
            set { printerName = value; }
        }

        DateTime timeQueued;
        public DateTime TimeQueued
        {
            get { return timeQueued; }
            set { timeQueued = value; }
        }

        string printServer;
        public string PrintServer
        {
            get { return printServer; }
            set { printServer = value; }
        }

        PrintQueueStatus lastStatus = new PrintQueueStatus();
        public PrintQueueStatus LastStatus
        {
            get { return lastStatus; }
            set { lastStatus = value; }
        }

        private string printerPath;
        public string PrinterPath
        {
            get { return printerPath; }
            set { printerPath = value; }
        }

    }
}
